import 'dart:convert';
import 'dart:io';
import 'package:appflutterhomeschool/common/common.dart';
import 'package:appflutterhomeschool/common/global.dart';
import 'package:dio/dio.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:logger/logger.dart';
import 'api_service.dart';

/*
 * 封装 restful 请求
 *
 * GET、POST、DELETE、PATCH
 * 主要作用为统一处理相关事务：
 *  - 统一处理请求前缀；
 *  - 统一打印请求信息；
 *  - 统一打印响应信息；
 *  - 统一打印报错信息；
 */
class DioUtils {
  static DioUtils _instance;

  ///单例模式
  static DioUtils getInstance({int tag}) {
    if (tag == null) {
      return _instance == null ? _instance = DioUtils() : _instance;
    } else {
      return _instance = DioUtils();
    }
  }

  BaseOptions options;
  Dio dio;

  DioUtils() {
    options = BaseOptions(
      baseUrl: Constant.httpUrl,
      //连接服务器超时时间，单位是毫秒.
      connectTimeout: 1000 * 45,
      //响应流上前后两次接受到数据的间隔，单位为毫秒。
      receiveTimeout: 1000 * 45,
//      application/x-www-form-urlencoded格式编码,
      contentType: Headers.formUrlEncodedContentType,
      headers: {
        "token": Constant.getToken(),
      },
    );
    dio = Dio(options);
    dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志
    //拦截器   暂时没有用到的地方
    dio.interceptors.add(
      InterceptorsWrapper(onRequest: (RequestOptions options) async {
        // 如果你想完成请求并返回一些自定义数据，可以返回一个`Response`对象或返回`dio.resolve(data)`。
        // 这样请求将会被终止，上层then会被调用，then中返回的数据将是你的自定义数据data.
        // 如果你想终止请求并触发一个错误,你可以返回一个`DioError`对象
        return options;
      }, onResponse: (Response response) async {
        // 在返回响应数据之前做一些预处理
        return response;
      }, onError: (DioError e) async {
        return e;
      }),
    );
  }

  //get请求
  get(String url, Map<String, dynamic> params, Function successCallBack,
      Function errorCallBack) async {
    _requstHttp(url, successCallBack, 'get', params, errorCallBack);
  }

  //post请求
  post(String url, Map<String, dynamic> params, Function successCallBack,
      Function errorCallBack) async {
    _requstHttp(url, successCallBack, "post", params, errorCallBack);
  }

  _requstHttp(String url, Function successCallBack,
      [String method,
      Map<String, dynamic> params,
      Function errorCallBack]) async {
    Response response;
    try {
      if (method == 'get') {
        if (params != null && params.toString().isNotEmpty) {
          response = await dio.get(url, queryParameters: params);
        } else {
          response = await dio.get(url);
        }
      } else if (method == 'post') {
        if (params != null && params.toString().isNotEmpty) {
          response = await dio.post(url, data: params);
        } else {
          response = await dio.post(url);
        }
      }
    } on DioError catch (error) {
      // 请求错误处理
      Response errorResponse;
      if (error.response != null) {
        errorResponse = error.response;
      } else {
        errorResponse = new Response(statusCode: 666);
      }
      // 请求超时
      if (error.type == DioErrorType.CONNECT_TIMEOUT) {
        errorResponse.statusCode = ResultCode.CONNECT_TIMEOUT;
      }
      // 一般服务器错误
      else if (error.type == DioErrorType.RECEIVE_TIMEOUT) {
        errorResponse.statusCode = ResultCode.RECEIVE_TIMEOUT;
      }

      // debug模式才打印
      if (Global.isDebug) {
        print('请求异常: ' + error.toString());
        print('请求异常url: ' + url);
        print('请求头: ' + dio.options.headers.toString());
//        print('method: ' + dio.options.method);
      }
      _error(errorCallBack, error.message);
      return '';
    }
    // debug模式打印相关数据
    if (Global.isDebug) {
      print('请求url: ' + url);
      print('请求头: ' + dio.options.headers.toString());
      if (params != null) {
        print('请求参数: ' + params.toString());
      }
      if (response != null) {
        print('返回参数: ' + response.toString());
      }
    }
    Map<String, dynamic> dataMap;
    if (response.data is String) {
      dataMap = json.decode(response.data);
    } else {
      dataMap = response.data;
    }
    Logger().d(dataMap);
    if (dataMap != null && dataMap['code'] == 1) {
      if (dataMap["data"] == null) {
        return;
      }
      successCallBack(dataMap);
    } else {
      Fluttertoast.showToast(msg: dataMap["message"]);
      _error(
          errorCallBack,
          '错误码：' +
              dataMap['errorCode'].toString() +
              '，' +
              response.data.toString());
    }
  }

  _error(Function errorCallBack, String error) {
    if (errorCallBack != null) {
      errorCallBack(error);
    }
  }

  /// 上传文件
  Future uploadFile(File file, {onSendProgress}) async {
    String fileName = file.path.split('/').last;
    print('file = $file');
    print('fileName = $fileName');

    BaseOptions bo = BaseOptions(
      baseUrl: Constant.httpUrl,
      //连接服务器超时时间，单位是毫秒.
      connectTimeout: 1000 * 45,
      //响应流上前后两次接受到数据的间隔，单位为毫秒。
      receiveTimeout: 1000 * 45,
    );
    bo.headers['Content-Type'] = 'multipart/form-data';
    Dio dioFile = Dio(bo);
    dioFile.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志
    FormData formData = FormData.fromMap({
      "file": await MultipartFile.fromFile(file.path, filename: fileName),
    });
    Response response =
        await dioFile.post(ApiService.uploadFile, data: formData);
    if (response.data == null) {
      return;
    }
    Logger().d(jsonDecode(response.data));
    Map<String, dynamic> map = jsonDecode(response.data);
    if (map["code"] != 1) {
      Fluttertoast.showToast(msg: map["message"]);
      return;
    }
    return map["data"];
  }
}

class Dio2Utils {
  static Dio2Utils _instance;

  ///单例模式
  static Dio2Utils getInstance({int tag}) {
    if (tag == null) {
      return _instance == null ? _instance = Dio2Utils() : _instance;
    } else {
      return _instance = Dio2Utils();
    }
  }

  BaseOptions options;
  Dio dio;

  Dio2Utils() {
    options = BaseOptions(
      baseUrl: Constant.httpUrl,
      //连接服务器超时时间，单位是毫秒.
      connectTimeout: 1000 * 45,
      //响应流上前后两次接受到数据的间隔，单位为毫秒。
      receiveTimeout: 1000 * 45,
//      application/x-www-form-urlencoded格式编码,
      contentType: Headers.jsonContentType,
      headers: {
        "token": Constant.getToken(),
      },
    );
    dio = Dio(options);
    dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志
    //拦截器   暂时没有用到的地方
    dio.interceptors.add(
      InterceptorsWrapper(onRequest: (RequestOptions options) async {
        // 如果你想完成请求并返回一些自定义数据，可以返回一个`Response`对象或返回`dio.resolve(data)`。
        // 这样请求将会被终止，上层then会被调用，then中返回的数据将是你的自定义数据data.
        // 如果你想终止请求并触发一个错误,你可以返回一个`DioError`对象
        return options;
      }, onResponse: (Response response) async {
        // 在返回响应数据之前做一些预处理
        return response;
      }, onError: (DioError e) async {
        return e;
      }),
    );
  }

  //get请求
  get(String url, Map<String, dynamic> params, Function successCallBack,
      Function errorCallBack) async {
    _requstHttp(url, successCallBack, 'get', params, errorCallBack);
  }

  //post请求
  post(String url, Map<String, dynamic> params, Function successCallBack,
      Function errorCallBack) async {
    _requstHttp(url, successCallBack, "post", params, errorCallBack);
  }

  _requstHttp(String url, Function successCallBack,
      [String method,
      Map<String, dynamic> params,
      Function errorCallBack]) async {
    Response response;
    try {
      if (method == 'get') {
        if (params != null && params.toString().isNotEmpty) {
          response = await dio.get(url, queryParameters: params);
        } else {
          response = await dio.get(url);
        }
      } else if (method == 'post') {
        if (params != null && params.toString().isNotEmpty) {
          response = await dio.post(url, data: params);
        } else {
          response = await dio.post(url);
        }
      }
    } on DioError catch (error) {
      // 请求错误处理
      Response errorResponse;
      if (error.response != null) {
        errorResponse = error.response;
      } else {
        errorResponse = new Response(statusCode: 666);
      }
      // 请求超时
      if (error.type == DioErrorType.CONNECT_TIMEOUT) {
        errorResponse.statusCode = ResultCode.CONNECT_TIMEOUT;
      }
      // 一般服务器错误
      else if (error.type == DioErrorType.RECEIVE_TIMEOUT) {
        errorResponse.statusCode = ResultCode.RECEIVE_TIMEOUT;
      }

      // debug模式才打印
      if (Global.isDebug) {
        print('请求异常: ' + error.toString());
        print('请求异常url: ' + url);
        print('请求头: ' + dio.options.headers.toString());
//        print('method: ' + dio.options.method);
      }
      _error(errorCallBack, error.message);
      return '';
    }
    // debug模式打印相关数据
    if (Global.isDebug) {
      print('请求url: ' + url);
      print('请求头: ' + dio.options.headers.toString());
      if (params != null) {
        print('请求参数: ' + params.toString());
      }
      if (response != null) {
        print('返回参数: ' + response.toString());
      }
    }
    Map<String, dynamic> dataMap;
    if (response.data is String) {
      dataMap = json.decode(response.data);
    } else {
      dataMap = response.data;
    }
    Logger().d(dataMap);
    if (dataMap != null && dataMap['code'] == 1) {
      if (dataMap["data"] == null) {
        return;
      }
      successCallBack(dataMap);
    } else {
//      Fluttertoast.showToast(msg: dataMap["message"]);
      _error(
          errorCallBack,
          '错误码：' +
              dataMap['errorCode'].toString() +
              '，' +
              response.data.toString());
    }
  }

  _error(Function errorCallBack, String error) {
    if (errorCallBack != null) {
      errorCallBack(error);
    }
  }

  /// 检测活动相册下是否有红点
  Future post1(String url, Map<String, dynamic> map) async {
    Response response = await dio.post(url, data: map);
    Logger().d(response.data);
    return jsonDecode(response.data);
  }
}

/*
 * dio网络请求失败的回调错误码
 */
class ResultCode {
  //正常返回是1
  static const SUCCESS = 1;

  //异常返回是0
  static const ERROR = 1;

  /// When opening  url timeout, it occurs.
  static const CONNECT_TIMEOUT = -1;

  ///It occurs when receiving timeout.
  static const RECEIVE_TIMEOUT = -2;

  /// When the server response, but with a incorrect status, such as 404, 503...
  static const RESPONSE = -3;

  /// When the request is cancelled, dio will throw a error with this type.
  static const CANCEL = -4;

  /// read the DioError.error if it is not null.
  static const DEFAULT = -5;
}
